// /* demo代码 */
// // 模拟管道文件
// // 1. 以读写方式打开同一个文件
// // 2. 创建子进程
// // 3. 关闭不必要的fd，确保为单向通信

// #include <iostream>
// #include <unistd.h>
// #include <assert.h>
// #include <string.h>
// #include <sys/types.h>
// #include <sys/wait.h>

// using namespace std;

// int main()
// {
//     // 1.创建管道
//     int pipefd[2] = {0};  // 输出型参数    pideid[0(嘴巴，读)]：读端   pideid[1(钢笔，写)]：写端
//     int n = pipe(pipefd); // 成功返回0， 失败返回-1
//     assert(n != 1);       // assert: 1.release 版本下会被删除，若没有下面的void(n),在release版本时会报错：n未被使用
//     (void)n;

// // 条件编译，相当于注释了，若在makefile里的g++那行命令的最后加上-DDEBUG，就可以调试这一行,注意是  DD EBUG,不是  D  EBUG
// #ifdef DEBUG
//     cout << pipefd[0] << endl; // 3
//     cout << pipefd[1] << endl; // 4
// #endif

//     // 2.创建子进程
//     pid_t id = fork();
//     assert(id != -1);

//     if (id == 0)
//     {
//         // 子进程
//         // 3.创建单向信道，父进程写入，子进程读取
//         // 3.1 关闭不必要的fd ------- 写 ------ pipefd[1]
//         close(pipefd[1]);

//         char buffer[1024];
//         while (true)
//         {
//             ssize_t s = read(pipefd[0], buffer, sizeof(buffer)); // 疯狂读管道文件传输过来的信息
//             if (s > 0)                                           // 读取成功
//             {
//                 buffer[s] = 0; // 因为读入的时候没有'\0',要自己加

//                 cout << "child get message from father:" << buffer << endl;
//             }
//         }

//         close(pipefd[0]);
//         // 可写可不写，因为进程退出了fd就会自动关闭

//         exit(0);
//         // 让代码不执行父进程后面的代码
//     }

//     // 一定是父进程
//     //  3.创建单向信道，父进程写入，子进程读取
//     //  3.2 关闭不必要的fd ------- 读 ------ pipefd[0]
//     close(pipefd[0]);

//     string message = "我是父进程，我在发信息";
//     char send_buffer[1024];
//     int count = 0;
//     // 疯狂写
//     while (true)
//     {
//         // printf一系列的函数不仅可以往显示器写，还可以往文件里写(fprintf)，还可以往字符串写(snprintf)
//         snprintf(send_buffer, sizeof(send_buffer), "%s[%d]", message.c_str(), count++);
//         /* 注意要传参的是const char* */
//         /* int snprintf(char *str, size_t size, const char *format, ...); */

//         // 疯狂写
//         write(pipefd[1], send_buffer, strlen(send_buffer));

//         // 故意sleep
//         sleep(1);
//     }

//     close(pipefd[1]);
//     pid_t ret = waitpid(id, NULL, 0);
//     assert(ret != -1); // assert的用法：当括号内的条件满足时才进行后面的代码，括号内的条件不满足就终止
//     (void)ret;

//     return 0;
// }

// // 虽然子进程没有设置一秒打印一次，但是仍然是一秒一秒地显示，因为要等待父进程写入
















/* 自己写demo代码，看自己是否理解管道 */

// 管道的实现
// 1.两个fd(读和写)指向同一文件(pipefd)
// 2.创建子进程
// 3.关闭各自不需要的pipefd，以确保信道的单向性

#include <iostream>
#include <unistd.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main()
{
    // 1.两个fd(读和写)指向同一文件(pipefd)
    int pipefd[2] = {0};

    int n = pipe(pipefd);
    // pipe:
    // 1.返回值：成功为0， 失败为-1
    // 2.pipefd：输出型参数

    assert(n != -1);
    // 括号中不满足就退出
    (void)n;
    // 在release版本下，assert会被删掉，若没有这一行代码，就会造成变量未使用的报警

    // 2.创建子进程
    pid_t id = fork();
    if (id == 0)
    {
        // 子进程
        // 关掉写
        close(pipefd[1]);

        // 读
        char buffer[1024];
        while (true)
        {
            // read：成功返回读取的字符数，失败返回-1
            ssize_t n = read(pipefd[0], buffer, sizeof(buffer));
            assert(n != -1);

            buffer[n] = 0;// 因为文件中读取的字符末尾没有0，因为不是C语言

            cout << "child get the massage from father: " << buffer << endl;
        }

        close(pipefd[0]);
        // 可写可不写，因为退出后会自动关闭所有文件描述符

        // 退出，不执行后面的父进程代码
        exit(0);
    }

    // 父进程
    // 关掉读
    close(pipefd[0]);
    string message = "我是父进程，我在给你发信息";
    char send_buffer[1024];

    while (true)
    {
        // printf不仅可以往显示器写，还可以往文件里写(fprintf), 还可以往字符串写(snprintf)
        // int snprintf(char *str, size_t size, const char *format, ...);
        snprintf(send_buffer, sizeof(send_buffer), "%s", message.c_str());
                                                        /* c_str():将字符串转化为const char* */
        // 写
        write(pipefd[1], send_buffer, sizeof(send_buffer));
        sleep(1);
    }

    pid_t ret = waitpid(id, NULL, 0);
    // 0表示成功返回，-1表示错误返回
    assert(ret != -1);

    //关闭写文件描述符
    close(pipefd[1]);

    return 0;
}